////////////////////////////////////////////////////////////////////////////////
/// @brief tasks used to establish connections
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Achim Brandt
/// @author Copyright 2009-2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#ifndef TRIAGENS_SCHEDULER_CONNECTION_TASK_H
#define TRIAGENS_SCHEDULER_CONNECTION_TASK_H 1

#include "Scheduler/SocketTask.h"

namespace triagens {
  namespace rest {

////////////////////////////////////////////////////////////////////////////////
/// @ingroup Scheduler
/// @brief task used to establish connections
////////////////////////////////////////////////////////////////////////////////

    class ConnectionTask : public SocketTask {
      private:
        ConnectionTask (ConnectionTask const &);
        ConnectionTask& operator= (ConnectionTask const &);

      public:

////////////////////////////////////////////////////////////////////////////////
/// @brief error status
////////////////////////////////////////////////////////////////////////////////

        enum error_e {
          ERROR_NO_ERROR,
          ERROR_CONNECTION_FAILURE,
          ERROR_CONNECTION_TIMEOUT,
          ERROR_CONNECTION_CLOSED,
          ERROR_EXECUTION_FAILURE,
          ERROR_EXECUTION_TIMEOUT,
          ERROR_GENERAL_ERROR,
          ERROR_SHUTDOWN_IN_PROGRESS
        };

      public:

////////////////////////////////////////////////////////////////////////////////
/// @brief connection to given hostname and port
////////////////////////////////////////////////////////////////////////////////

        ConnectionTask (string const& hostname, int port);

////////////////////////////////////////////////////////////////////////////////
/// @brief connection to given hostname and port
////////////////////////////////////////////////////////////////////////////////

        ConnectionTask (string const& hostname, int port, double connectTimeout);

////////////////////////////////////////////////////////////////////////////////
/// @brief connection to given hostname and port
////////////////////////////////////////////////////////////////////////////////

        ConnectionTask (string const& hostname, int port, double connectTimeout, double commTimeout);

////////////////////////////////////////////////////////////////////////////////
/// @brief connection using a given socket
////////////////////////////////////////////////////////////////////////////////

        ConnectionTask (socket_t fd);

////////////////////////////////////////////////////////////////////////////////
/// @brief connection using a given socket
////////////////////////////////////////////////////////////////////////////////

        ConnectionTask (socket_t fd, double commTimeout);

      public:

////////////////////////////////////////////////////////////////////////////////
/// @brief checks if hostname is resolved
///
/// Note that you can use this method only before the call to registerTask.
////////////////////////////////////////////////////////////////////////////////

        bool isResolved () const {
          return resolved;
        }

////////////////////////////////////////////////////////////////////////////////
/// @brief checks if the socket is connected
///
/// Note that you can use this method only before the call to registerTask.
////////////////////////////////////////////////////////////////////////////////

        bool isConnected () const {
          return state == STATE_CONNECTED;
        }

////////////////////////////////////////////////////////////////////////////////
/// @brief checks if the socket is created
///
/// Note that you can use this method only before the call to registerTask.
////////////////////////////////////////////////////////////////////////////////

        bool isConnecting () const {
          return state == STATE_CONNECTED || state == STATE_CONNECTION_INPROGRESS;
        }

        int getPort () const {
          return port;
        }

      protected:

////////////////////////////////////////////////////////////////////////////////
/// @brief called by the task to indicate connection success
////////////////////////////////////////////////////////////////////////////////

        virtual bool handleConnected () = 0;

////////////////////////////////////////////////////////////////////////////////
/// @brief called by the task to indicate connection failure
////////////////////////////////////////////////////////////////////////////////

        virtual bool handleConnectionFailure () = 0;

////////////////////////////////////////////////////////////////////////////////
/// @brief called by the task to indicate a timeout
////////////////////////////////////////////////////////////////////////////////

        virtual bool handleConnectionTimeout () = 0;

////////////////////////////////////////////////////////////////////////////////
/// @brief called by the task to indicate a timeout
////////////////////////////////////////////////////////////////////////////////

        virtual bool handleCommunicationTimeout () = 0;

      protected:

////////////////////////////////////////////////////////////////////////////////
/// @brief destructs a connection task
////////////////////////////////////////////////////////////////////////////////

        ~ConnectionTask ();

      protected:

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
///
/// Note that you should only call registerTask if the address has been
/// resolved.
////////////////////////////////////////////////////////////////////////////////

        void setup (Scheduler*, EventLoop);

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////

        void cleanup ();

////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////

        bool handleEvent (EventToken, EventType);

      protected:

////////////////////////////////////////////////////////////////////////////////
/// @brief timer event
////////////////////////////////////////////////////////////////////////////////

        EventToken watcher;

      protected:

////////////////////////////////////////////////////////////////////////////////
/// @brief state of the state machine
////////////////////////////////////////////////////////////////////////////////

        enum state_e {
          STATE_UNCONNECTED,
          STATE_CONNECTION_INPROGRESS,
          STATE_CONNECTED
        };

////////////////////////////////////////////////////////////////////////////////
/// @brief the state of the socket
////////////////////////////////////////////////////////////////////////////////

        state_e state;

////////////////////////////////////////////////////////////////////////////////
/// @brief true, if no request is in progress
///
/// Note that the sub-class must set/reset the idle flag in handleEvent.
////////////////////////////////////////////////////////////////////////////////

        bool idle;

////////////////////////////////////////////////////////////////////////////////
/// @brief connection timeout in seconds
////////////////////////////////////////////////////////////////////////////////

        double connectTimeout;

////////////////////////////////////////////////////////////////////////////////
/// @brief communication timeout in seconds
////////////////////////////////////////////////////////////////////////////////

        double commTimeout;

      private:
        void resolveAddress ();
        void connectSocket ();
        bool handleConnectionEvent (EventToken token, EventType event);
        bool handleCommunicationEvent (EventToken token, EventType event);

      private:
        string const hostname;
        int const port;

        char* netaddress;
        size_t netaddressLength;

        bool resolved;
    };
  }
}

#endif
